﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;

namespace Batty
{
    public class Bat
    {
        private Pondhawk game;
        private Model model;
        public int leftwing;
        public int rightwing;
        private float wingAngle = 0;
        private bool flapping = false;
        private LaserFire laserFire;
        private Vector3[] laserOne = { new Vector3(0.0f, 0.0f, 800.0f) };
        private int laserNext = 0;
        private int laserFront = 0;
        private bool stopFlap;

        /// <summary>
        /// Current position
        /// </summary>
        private Vector3 position = new Vector3(0, 200, 0);

        /// <summary>
        /// Compass heading (radians, 0 is Z direction)
        /// </summary>
        private float azimuth = 0;

        /// <summary>
        /// Climb angle (radians, 0 is level)
        /// </summary>
        private float elevation = 0;

        /// <summary>
        /// How fast we are going (cm/sec)
        /// </summary>
        private float speed = 0;

        /// <summary>
        /// Flap in cm/sec^2
        /// </summary>
        private float flap = 0;

        /// <summary>
        ///  Decelleration due to drag
        /// </summary>
        private const float Drag = 10;

        /// <summary>
        /// Maximum flap (cm/sec^2)
        /// </summary>
        private const float MaxFlap = 500;

        /// <summary>
        /// The current turning rate in radians per second
        /// Effectively the azimuth change rate
        /// </summary>
        private float turnRate = 0;

        /// <summary>
        /// The maximum turning rate
        /// </summary>
        private const float MaxTurnRate = (float)Math.PI / 2.0f;

        /// <summary>
        /// The current turning rate in radians per second
        /// Effectively the azimuth change rate
        /// </summary>
        private float elevRate = 0;

        /// <summary>
        /// The maximum turning rate
        /// </summary>
        private const float MaxElevRate = (float)Math.PI / 2.0f;

        /// <summary>
        /// The current turning rate in radians per second
        /// Effectively the azimuth change rate
        /// </summary>
        private float bankRate = 0;

        /// <summary>
        /// The maximum turning rate
        /// </summary>
        private const float MaxBankRate = (float)Math.PI;

        /// <summary>
        /// Ship orientation as a quaternion
        /// </summary>
        Quaternion orientation = Quaternion.Identity;


        /// <summary>
        /// The current ship flap
        /// </summary>
        public float Flap { get { return flap; } set { flap = value; } }

        /// <summary>
        /// Turning rate in radians per second
        /// </summary>
        public float TurnRate { get { return turnRate; } set { turnRate = value; } }

        public float ElevRate { get { return elevRate; } set { elevRate = value; } }

        public float BankRate { get { return bankRate; } set { bankRate = value; } }

        public LaserFire LaserFire { get { return laserFire; } }

        public Vector3 Position { get { return position; } set { position = value; } }

        public bool Flapping { get { return flapping; } set { flapping = value; } }

        public Model Model { get { return model; } }

        /// <summary>
        /// The current ship transformation
        /// </summary>
        public Matrix Transform
        {
            get
            {
                return Matrix.CreateFromQuaternion(orientation) *
                        Matrix.CreateTranslation(position);
            }
        }


        public Bat(Pondhawk game)
        {

            this.game = game;
            laserFire = new LaserFire(game);
        }

        /// <summary>
        /// This function is called to load content into this component
        /// of our game.
        /// </summary>
        /// <param name="content">The content manager to load from.</param>
        public void LoadContent(ContentManager content)
        {
            model = content.Load<Model>("bat-rigid");
            leftwing = model.Bones.IndexOf(model.Bones["LeftWing1"]);
            rightwing = model.Bones.IndexOf(model.Bones["RightWing1"]);
            laserFire.LoadContent(content);

        }

        /// <summary>
        /// This function is called to update this component of our game
        /// to the current game time.
        /// </summary>
        /// <param name="gameTime"></param>
        public void Update(GameTime gameTime)
        {

            float wingTime = .5f;

            if (flapping && wingAngle < .90f && stopFlap ==false)
            {
                wingAngle += (float)(0.90 * gameTime.ElapsedGameTime.TotalSeconds / wingTime);
                if (wingAngle > 0.90f)
                {
                    wingAngle = .9f;
                    stopFlap = true;
                }

            }
            else if (flapping && wingAngle > 0 && stopFlap ==true)
            {
                wingAngle -= (float)(0.90 * gameTime.ElapsedGameTime.TotalSeconds / wingTime);
                if (wingAngle < 0)
                {
                    wingAngle = 0;
                    stopFlap = false;
                }
                    
            }
            else if (!flapping && wingAngle > 0)
            {
                wingAngle -= (float)(0.90 * gameTime.ElapsedGameTime.TotalSeconds / wingTime);
                if (wingAngle < 0)
                {
                    wingAngle = 0;
                    stopFlap = false;
                }
            }

            float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;

            float turnAngle = turnRate * MaxTurnRate * delta;
            orientation *= //Quaternion.CreateFromAxisAngle(new Vector3(0, 0, 1), -turnAngle) *
                           Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), turnAngle);
            orientation *= Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), ElevRate * MaxElevRate * delta);
            orientation.Normalize();

            //
            // Position updates
            //

            float acceleration = flap * MaxFlap - Drag * speed;
            speed += acceleration * (float)gameTime.ElapsedGameTime.TotalSeconds;

            Matrix transform = Matrix.CreateFromQuaternion(orientation);

            Vector3 directedFlap = Vector3.TransformNormal(new Vector3(0, 0, 1), transform);
            position += directedFlap * speed * (float)gameTime.ElapsedGameTime.TotalSeconds;
            if (position.Y <= 5)
                position.Y = 5;
            
               laserFire.Update(gameTime);
            System.Diagnostics.Trace.WriteLine("Positon"+position.X.ToString()+"  " +position.Y.ToString()+ "   " +position.Z.ToString());
        }


        /// <summary>
        /// This function is called to draw this game component.
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="gameTime"></param>
        public void Draw(GraphicsDeviceManager graphics, GameTime gameTime)
        {

            DrawModel(graphics, model, Transform);
            laserFire.Draw(graphics, gameTime);
        }

        private void DrawModel(GraphicsDeviceManager graphics, Model model, Matrix world)
        {
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);

            //transforms[leftwing] = Matrix.CreateRotationY(wingAngle) * transforms[leftwing];
            //transforms[rightwing] = Matrix.CreateRotationY(-wingAngle) * transforms[rightwing];

            int count = model.Bones.Count;
            for (int i = 0; i < count; i++)
            {
                if (i == leftwing)
                    transforms[i] = Matrix.CreateRotationY(wingAngle);
                else if (i == rightwing)
                    transforms[i] = Matrix.CreateRotationY(-wingAngle);
                else
                    transforms[i] = Matrix.Identity;

                ModelBone bone = model.Bones[i];
                if (bone.Parent == null)
                {
                    transforms[i] *= bone.Transform;
                }
                else
                {
                    transforms[i] *=  bone.Transform * transforms[bone.Parent.Index];
                }
            }

            foreach (ModelMesh mesh in model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    BetterBasic(effect);
                    effect.World = transforms[mesh.ParentBone.Index] * world;
                    effect.View = game.Camera.View;
                    effect.Projection = game.Camera.Projection;
                }
                mesh.Draw();
            }
        }
        public void FireLaser()
        {
            //System.Diagnostics.Trace.WriteLine("Fire!");
            //if (wingAngle < 0.20f)
            //    return;

           // game.SoundBank.PlayCue("echo_ring");

            laserNext = (laserNext + 1) % 4;

            laserFront = 0;

            System.Diagnostics.Trace.WriteLine("Fire " + laserNext.ToString() + "!");

            Vector3 position = Vector3.Transform(laserOne[laserFront], Transform);

            Matrix orientation = Transform;
            orientation.Translation = Vector3.Zero;

            Vector3 direction = Vector3.TransformNormal(new Vector3(0, 0, 1), orientation);
            //System.Diagnostics.Trace.WriteLine("From: " + position.ToString() +
            //" Direction: " + direction.ToString());

            laserFire.FireLaser(position, orientation, speed);

        }
        public void BetterBasic(BasicEffect effect)
        {
            effect.LightingEnabled = true;
            effect.AmbientLightColor = new Vector3(0.325333332f, 0.09882354f, 0.0819608f);

            effect.DirectionalLight0.DiffuseColor = new Vector3(0, 0, 1);
            effect.DirectionalLight0.SpecularColor = effect.DirectionalLight0.DiffuseColor;
            effect.DirectionalLight0.Direction = new Vector3(0, 1, 0);
            effect.DirectionalLight0.Enabled = true;

            effect.DirectionalLight1.DiffuseColor = new Vector3(0, 0, 1);
            effect.DirectionalLight1.SpecularColor = effect.DirectionalLight1.DiffuseColor;
            effect.DirectionalLight1.Direction = Vector3.Normalize(new Vector3(-1, -1, -1));
            effect.DirectionalLight1.Enabled = true;

            effect.DirectionalLight2.DiffuseColor = new Vector3(0, 0, 1);
            effect.DirectionalLight2.SpecularColor = effect.DirectionalLight2.DiffuseColor;
            effect.DirectionalLight2.Direction = Vector3.Normalize(new Vector3(-1, -1, 1));
            effect.DirectionalLight2.Enabled = true;

        }
    }
}
